// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: qiang

#pragma once

#include <memory>
#include <sstream>
#include <string>
#include <utility>

#include "coin2/exchange/base/order/rate_tracker.h"

namespace coin2::exchange::crypto_common::api_util {

enum QueryType : int {
  UNKNOWN = 0,
  ACCOUNT = 1,
  HIST_ORDER = 2,
  FILL_ORDER = 3,
  OPEN_ORDER = 4,
  PLACE_ORDER = 5,
  CANCEL_ORDER = 6,
  POSITION = 7
};

class CryptoRateTracker : public coin2::exchange::base::order::IRateTracker {
 public:
  using RateTracker = coin2::exchange::base::order::RateTracker;
  using ActionType = coin2::exchange::base::order::IRateTracker::ActionType;
  using RateLimitReportWriter = coin2::exchange::base::order::RateLimitReportWriter;
  using FixedWindowRateTracker = coin2::exchange::base::order::FixedWindowRateTracker;
  using RateLimitInfoCryptoV1 = coin2::exchange::order::RateLimitInfoCryptoV1;

 public:
  explicit CryptoRateTracker(
      const MarketExchangeApi& mea,
      const coin2::exchange::order::OrderConnectionConfig& cfg);

  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type) override { return true; };
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type) override {}
  void ParseRateLimitInfo(const std::shared_ptr<AsioHttpContext>&, ActionType) override {}
  void SetRateLimitInfo(int32_t rate_limit, int32_t rate_limit_remaining) override { CHECK(false); }
  void SetRateLimitInfoString(const google::protobuf::Message* message) override;
  void WriteRateLimitReportMessage(OrderLoggerCB callback) override;
  double GetMaxRateUsedRatio(int64_t timestamp, ActionType action_type) override;

  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type, QueryType query_type);
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type, QueryType query_type);

 private:
  void InitRateTracker(
      RateLimitInfoCryptoV1* rate_config,
      std::string_view mea_str,
      std::string_view example);

  double GetMaxRateUsedRatio(
      const std::unique_ptr<FixedWindowRateTracker>& rate_tracker,
      int64_t timestamp) {
    double rate_used_ratios = 0.0;
    if (rate_tracker) {
      auto max_reqs = rate_tracker->MaxReq();
      auto remaining = rate_tracker->GetRemaining(timestamp);
      if (max_reqs > 0) {
        rate_used_ratios = 1.0 * (max_reqs - remaining) / max_reqs;
      }
    }
    return rate_used_ratios;
  }

 private:
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_account_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_position_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_openorder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_history_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_fillorder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_placeorder_;
  std::unique_ptr<FixedWindowRateTracker> rate_tracker_cancelorder_;
  std::unique_ptr<RateLimitReportWriter> rate_limit_report_writer_ = {};
  const MarketExchangeApi& mea_;
};

}  // namespace coin2::exchange::crypto_common::api_util
